package com.nts.bupna.util;

import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import org.antlr.runtime.tree.Tree;

import com.nts.bupna.BLexer;

public class AstPrinter {
    public static void showAst(Tree ast, PrintWriter pw) {
        final ArrayList<Tree> parents = new ArrayList<Tree>();
        showAst(parents, pw, ast);
        pw.flush();
    }

    private static void showAst(ArrayList<Tree> parents, PrintWriter pw, Tree ast) {
        if (ast == null) {
            pw.println("AST is null!");
            return;
        }

        for (int i = 0; i < parents.size(); i++) {
            final Tree parent = parents.get(i);
            if (parent.getChildIndex() == parent.getChildCount() - 1) {

                pw.print("   ");
            } else {
                pw.print(" | ");
            }
        }

        if (ast.getChildIndex() == ast.getChildCount() - 1) {
            pw.print(" \\-");
        } else {
            pw.print(" +-");
        }

        showNode(pw, ast);

        final ArrayList<Tree> newParents = new ArrayList<Tree>(parents);
        newParents.add(ast);

        for (int i = 0; i < ast.getChildCount(); i++) {
            final Tree child = ast.getChild(i);
            showAst(newParents, pw, child);
        }
        newParents.clear();
    }

    private static void showNode(PrintWriter pw, Tree ast) {
        final String s = nodeToString(ast, false);
        pw.println(s);
    }

    public static String nodeToString(Tree ast, boolean showClassName) {
        if (ast == null) {
            return "{null}";
        }
        final StringBuffer buf = new StringBuffer();
        buf.append("[").append(getTokenTypeName(ast.getType())).append("] ");
        if (showClassName) {
            buf.append(ast.getClass().getSimpleName()).append(": ");
        }

        buf.append("'");
        final String text = ast.getText();
        appendEscapedMultibyteChars(text, buf);
        buf.append("'");
        final String s = buf.toString();
        return s;
    }

    public static void appendEscapedMultibyteChars(String text, StringBuffer buf) {
        final char[] chars = text.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            final char aChar = chars[i];
            if (aChar > 256) {
                buf.append("\\u");
                buf.append(Integer.toHexString(aChar));
            } else
                buf.append(aChar);
        }
    }

    private static String getTokenTypeName(int type) {
        final Field[] fields = BLexer.class.getFields();
        String tokenTypeName = null;
        for (int i = 0; i < fields.length; i++) {
            final Field field = fields[i];
            if (Modifier.isStatic(field.getModifiers())) {
                Object value;
                try {
                    value = field.get(null);
                } catch (final IllegalAccessException e) {
                    throw new RuntimeException(e);
                }
                tokenTypeName = field.getName();
                if (value.equals(type)) {
                    return tokenTypeName;
                }
            }
        }

        return Integer.toString(type);
    }
}
